﻿#region License
/* ---------------------------------------------------------------- *
 *
 * This file is part of the API Concepts Framework
 * http://apiconceptsframework.codeplex.com
 *
 * Copyright (C) 2014 eva Kühn, Thomas Scheller, Vienna University of Technology
 * http://www.complang.tuwien.ac.at/eva
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 *
 * ---------------------------------------------------------------- */
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ApiConceptsFramework.FluentApi
{
	/// <summary>
	/// Extensions to retreive reflection infos from expressions.
	/// </summary>
	static class ExpressionReflectionExtensions
	{
		public static ConstructorInfo GetConstructorInfo<T>(this Expression<Func<T>> expr)
		{
			var body = expr.Body as NewExpression;
			if (body == null)
				throw new Exception("Expression is not a constructor");
			return body.Constructor;
		}

		public static MethodInfo GetMethodInfo(this LambdaExpression expr)
		{
			var body = expr.Body as MethodCallExpression;
			if (body == null)
				throw new Exception("Expression is not a method call");
			return body.Method;
		}

		public static List<MethodInfo> GetChainedMethodInfos(this LambdaExpression expr)
		{
			var methods = new List<MethodInfo>();
			var body = expr.Body as MethodCallExpression;
			if (body == null)
				throw new Exception("Expression is not a method call");
			while (true)
			{
				if (body == null)
					break;
				methods.Add(body.Method);
				//Console.WriteLine(body.Method);
				body = body.Object as MethodCallExpression;
			}
			methods.Reverse();
			return methods;
		}

		public static FieldInfo GetFieldInfo(this LambdaExpression expr)
		{
			var body = expr.Body as MemberExpression;
			if (body == null || !(body.Member is FieldInfo))
				throw new Exception("Expression is not a field access");
			return (FieldInfo)body.Member;
		}

		public static PropertyInfo GetPropertyInfo(this LambdaExpression expr)
		{
			var body = expr.Body as MemberExpression;
			if (body == null || !(body.Member is PropertyInfo))
			{
				var bodyIndexerMethod = expr.Body as MethodCallExpression;
				if (bodyIndexerMethod != null && bodyIndexerMethod.Method.Name == "get_Item")
				{
					var indexerProp =
						bodyIndexerMethod.Method.DeclaringType.GetProperties().FirstOrDefault(p => p.GetGetMethod() == bodyIndexerMethod.Method);
					if (indexerProp == null)
						throw new Exception("Expected to find indexer property for expression \"" + expr + "\", but couldn't find it!");
					return indexerProp;
				}
				else
					throw new Exception("Expression is not a property access");
			}
			return (PropertyInfo)body.Member;
		}
	}
}